Blog

JavaScript-Paketmanager: Yarn

Aug 21, 2023

Yarn ist ein JavaScript-Paketmanager und erfüllt als solcher ähnliche Aufgaben wie der Platzhirsch npm. Egal, für welchen der großen etablierten Paketmanager Sie sich entscheiden: Die Grundfunktionalität ist in jedem Werkzeug gewährleistet.

Sie unterscheiden sich vielleicht in der Implementierung und der Geschwindigkeit, doch das Installieren und Verwalten von Abhängigkeiten funktioniert in npm, Yarn und auch pnpm reibungslos. Der große Vorteil für uns als Benutzer:innen solcher Werkzeuge ist, dass es eine gewisse Konkurrenzsituation zwischen den einzelnen Implementierungen gibt, die kontinuierlich Verbesserungen und neue Features hervorbringt, von denen wir profitieren. In diesem Artikel fühlen wir Yarn etwas genauer auf den Zahn.

Geschichte von Yarn

Yarn ist der Versuch von Facebook beziehungsweise Meta, die Welt etwas besser, genauer gesagt, die Paketverwaltung von JavaScript sicherer, stabiler, reproduzierbarer und schneller zu machen. Und das hat gute Gründe. Zu dem Zeitpunkt, als Yarn entwickelt und veröffentlicht wurde, galt all das nicht für npm. Es war weder schnell noch zuverlässig noch sicher. Yarn hatte genau für diese Probleme eine Antwort und führte eine Datei mit dem Namen yarn.lock ein, in der nicht nur die direkt installierten Abhängigkeiten, sondern sämtliche Abhängigkeiten, also der gesamte Baum mit genauer Versionsnummer, der Paketquelle sowie einer Prüfsumme festgehalten wurden. Dieses Feature sorgte dafür, dass bei jeder Installation der Applikation immer die gleichen Pakete installiert wurden und somit sichergestellt war, dass die Applikation funktioniert. Dieses Konzept war so gut, dass es npm einige Zeit später selbst übernahm – ein klassisches Beispiel von den Vorteilen der Konkurrenz von Open-Source-Projekten, die zu einer allgemeinen Verbesserung der Lage führt.

Mit der Stabilisierung der Version 1 von Yarn wanderten immer mehr Nutzer:innen von npm ab und hin zu Yarn. Mit der Einführung der zweiten Version des Paketmanagers erwies das Team hinter Yarn kein glückliches Händchen. In den ersten Versionen gab es immer wieder kleinere Probleme und auch die Installation war wenig komfortabel, sodass das Projekt etwas an Schwung verlor und die Akzeptanz in der Community deutlich zurückging. Viele Entwickler:innen kehrten Yarn den Rücken und wanderten zurück zu npm, das zu dieser Zeit kontinuierlich weiterentwickelt wurde und immer wieder neue hilfreiche Features erhielt. Mittlerweile hat sich die Situation wieder beruhigt und Yarn hat eine stabile und treue Nutzerschaft um sich versammelt.

LUST AUF NOCH MEHR JAVASCRIPT?

Entdecken Sie Workshops vom 21. - 24. Oktober 2024

Installation und generelle Funktionsweise

Yarn ist der erste Paketmanager, der vollständig auf Corepack setzt, den internen Paketmanager für Paketmanager von Node.js. Für die Installation benötigen Sie also lediglich eine aktuelle Version von Node.js. Sollten Sie Node vor der Version 16.10 nutzen, müssten Sie theoretisch Corepack als externes Paket installieren. Bevor Sie jedoch damit beginnen, aktualisieren Sie lieber schleunigst Ihre Node-Version, da Version 16 am 11.09.2023 ihr Lebensende erreicht. Haben Sie Corepack und eine Node-Version, die Sie guten Gewissens nutzen können, können Sie mit dem folgenden Kommandos Yarn auf Ihrem System aktivieren:

Auf die neueste Version aktualisieren Sie mit yarn set version stable. Und damit ist eigentlich auch schon alles gesagt, was Sie über das Set-up von Yarn wissen müssen, und Sie können mit der Arbeit beginnen.

Die Arbeit mit Yarn

Ihr Projekt können Sie auf zwei Arten initialisieren: Entweder Sie nutzen yarn init oder yarn init -2. Was genau der Unterschied ist und wie sich das auf Ihr Projekt auswirkt, erfahren Sie später. Wichtig ist nur, dass Sie wissen, dass Sie in beiden Fällen mindestens eine package.json und eine yarn.lock erhalten. Die package.json-Datei ist die zentrale Beschreibungsdatei Ihrer Applikation, hier können Sie alle Metainformationen ablegen, die mit Ihrem Projekt zu tun haben. Das reicht von der aktuellen Versionsnummer über die Angabe der Lizenz und der Autor:innen bis hin zu den installierten Abhängigkeiten und Skripten für die Erledigung verschiedener Aufgaben. Während Sie die package.json von Hand modifizieren dürfen, sollten Sie tunlichst Ihre Finger von der yarn.lock lassen. Diese Datei ist maschinell generiert und enthält, wie bereits erwähnt, wichtige Informationen über die installierten Pakete. Generell sollten Sie sich bei der Paketverwaltung auf Ihren Paketmanager verlassen und nicht anfangen, seine Arbeit erledigen zu wollen. Das gilt vor allem für das Entfernen von Paketen. Zwar können Sie ein Paket aus der package.json und vielleicht sogar aus dem Dateisystem löschen, die durch dieses Paket installierten Abhängigkeiten und die Verweise in der yarn.lock bleiben erhalten und führen damit zu Inkonsistenzen. Für die Verwaltung von Paketen stellt Ihnen Yarn auf der Konsole eine Reihe von Kommandos zur Verfügung. Die wichtigsten davon finden Sie in der folgenden Liste:

  • yarn init: Das init-Kommando steht am Anfang der Entwicklung einer Applikation. Es hilft Ihnen bei der Erstellung der Basisstruktur für die Paketverwaltung in Ihrer Applikation.

  • yarn add: Mit yarn add fügen Sie neue Pakete zu Ihrer Applikation hinzu. Yarn installiert das Paket und alle seine Abhängigkeiten, sodass das Paket nach einer erfolgreichen Installation sofort einsatzfähig ist. Yarn erlaubt die Installation aus verschiedenen Quellen wie dem zentralen Paket-Repository, aber auch aus privaten Repositorys oder vom Dateisystem aus. Außerdem können Sie die Versionsnummer des Pakets angeben, das Sie installieren möchten.

  • yarn remove: Der einzig richtige Weg, ein einmal installiertes Paket wieder loszuwerden, führt über das remove-Kommando. Es stellt sicher, dass sowohl das Paket als auch alle seine Abhängigkeiten korrekt entfernt werden. Benötigt ein weiteres Paket eine installierte Abhängigkeit, wird diese selbstverständlich nicht gelöscht. Auch die yarn.lock und die package.json werden von Yarn konsistent gehalten und spiegeln zu jedem Zeitpunkt den Stand der installierten Pakete wider. Kümmern Sie sich selbst um die Deinstallation, müssten Sie sich um diese Aspekte ebenfalls kümmern.

  • yarn upgrade: Zur Verwaltung von Paketen gehört nicht nur das Installieren und Entfernen, sondern auch die Aktualisierung. In der package.json ist standardmäßig eine Spanne von erlaubten Versionsupdates festgehalten. Mehr Informationen über Versionsnummern finden Sie unter [1]. Die Limitierung der Versionsnummern soll dem Prinzip des Semantic Versioning Rechnung tragen. Es sagt aus, dass bei einem Sprung in der Major-Version Breaking Changes zu erwarten sind. Hier müssen Sie also sehr vorsichtig vorgehen. Ein leichtfertiges yarn upgrade könnte hier fatale Folgen haben. Steht eine solche Aktualisierung an, müssen Sie sich gesondert darum kümmern.

Sollten Sie Erfahrung mit npm haben, fragen Sie sich bestimmt, was denn mit dem install ist. Bei npm nutzen Sie diesen Befehl sowohl zur Installation von Paketen als auch zur Installation aller Pakete in einer bestehenden Applikation. Der Hintergrund ist hier, dass npm seine Abhängigkeiten in einem Verzeichnis mit dem Namen node_modules ablegt. Dieses Verzeichnis fügen Sie üblicherweise nicht zur Versionskontrolle hinzu, sodass Sie die Abhängigkeiten installieren müssen, wenn Sie sich den Quellcode aus dem Repository holen. Yarn verfügt ebenfalls über das install-Kommando, versucht jedoch über mehrere Features das node_modules-Verzeichnis loszuwerden. Von Beginn an hat Yarn zwischen dem Hinzufügen von Paketen und der Installation der Abhängigkeiten unterschieden. Aus diesem Grund gibt es für diese Zwecke mit add und install zwei verschiedene Kommandos. Diese Idee hat npm mittlerweile aufgegriffen und unterstützt mittlerweile auch das add-Kommando zum Hinzufügen eines neuen Pakets.

Besonderheiten von Yarn

Yarn weist einige Unterschiede zu npm auf, so können Sie beispielsweise auf das run-Kommando verzichten, wenn Sie ein Skript aus Ihrer package.json-Datei ausführen möchten. npm unterscheidet hier zwischen Standardskripten wie startstop oder test. Diese können Sie direkt aufrufen, also mit npm start. Falls Sie jedoch ein Skript mit einem anderen Namen definieren, wie beispielsweise build, müssen Sie das run-Kommando nutzen, also npm run build. Yarn vereinfacht Ihr Leben hier etwas, indem es auf run verzichtet und sich mit yarn build das Skript direkt ausführen lässt.

Ein npm-Feature, das viele Freunde hat, ist npx, ein Kommandozeilenwerkzeug, mit dem Sie ausführbare Pakete aufrufen können. Ein typisches Beispiel hierfür ist Create React App, ein Paket, mit dem Sie eine React-Applikation initialisieren können. Hier geht es weniger um die Initialisierung einer React-Applikation, sondern um die Tatsache, dass Sie das Create-React-App-Paket lediglich ein Mal im ganzen Lebenszyklus Ihrer Applikation benötigen. Sie könnten es zwar lokal auf Ihrem System installieren und dann gleich wieder löschen, sobald Sie Ihre Applikation initialisiert haben. Mit npx stellt sich dieses Problem nicht, da das Werkzeug zunächst nach dem Paket sucht. Falls es nicht gefunden wird, lädt npx es herunter, führt es aus und verwirft es wieder. In Yarn gibt es ein solch praktisches Feature nicht, was jedoch nicht weiter tragisch ist, da Sie die Fälle, in denen Sie npx tatsächlich brauchen, an einer Hand abzählen können. Falls Sie mit Yarn eine neue React-Applikation generieren möchten, erreichen Sie dies beispielsweise mit dem Kommando yarn create react-app my-app, was intern auch nur auf Create React App weiterleitet. Das Ganze ist nicht auf React beschränkt. Mit yarn create vite my-app können Sie mit Hilfe von Vite ein beliebiges Projekt starten. Das einzige Framework, das etwas aus der Reihe tanzt, ist Angular. Es nutzt seine eigene Kommandozeilenapplikation, die Sie mit dem Paketmanager Ihrer Wahl (also auch gerne mit Yarn) global installieren und dann die Entwicklung Ihres Projekts starten.

Yarn und node_modules

Wie schon erwähnt, steht Yarn mit dem node_modules-Verzeichnis auf Kriegsfuß. Und tatsächlich suchen Sie ein Verzeichnis mit diesem Namen in einer aktuellen mit Yarn initialisierten Applikation vergeblich. Das hat vor allem Performancegründe. Bei der Installation eines Pakets muss der Paketmanager das Paket herunterladen. Das liegt in der Regel als komprimierte Archivdatei vor, die entpackt wird. Im letzten Schritt kopiert npm den Code in das node_modules-Verzeichnis. Das Kopieren von Dateien kostet jedoch Zeit und genau die versucht Yarn hier einzusparen.

2018 hat Yarn ein neues Feature mit dem Namen Plug’n’Play eingeführt und sich damit vom node_modules-Verzeichnis verabschiedet. Stattdessen enthält Ihr Projekt eine Datei mit dem Namen .pnp.cjs, das Verweise auf Pakete und deren Versionen im Dateisystem enthält. Außerdem enthält es die Liste von Abhängigkeiten eines Pakets. Diese Informationen stellt Yarn dem Node Resolution Algorithm, also dem Algorithmus, mit dem Node die Pakete findet, zur Verfügung. Doch wie funktioniert das konkret? Angenommen, Sie möchten ein einfaches Node.js Backend mit Express implementieren. Im ersten Schritt initialisieren Sie Ihre Applikation mit yarn init. Anschließend fügen Sie Express mit yarn add express als Abhängigkeit hinzu. Dann müssen Sie nur noch Ihre Applikation implementieren. In Listing 1 sehen Sie die Minimalvariante einer solchen Umsetzung.

Listing 1: Minimale Express-Applikation

import { express } from 'express';
 
const app = express();
 
app.get('/', (req, res) => {
  res.send('Hello World!');
});
 
app.listen(8080);

Versuchen Sie, die Applikation aus alter Gewohnheit mit node index.js zu starten, quittiert Node das mit der Fehlermeldung „Cannot find module ‚express‘“. Die Modulauflösung scheint nicht zu funktionieren. Das liegt daran, dass Node nichts von der .pnp.cjs-Datei weiß. Nun haben Sie zwei Möglichkeiten: Sie können Ihre Applikation entweder mit yarn node index.js starten oder Sie fügen mit node index.js ein Startskript in Ihre package.json ein und starten die Applikation mit yarn start. In beiden Fällen sorgt Yarn nun dafür, dass Plug’n’Play reibungslos funktioniert.

Ganz wegdiskutieren lässt sich der Quellcode der Pakete jedoch nicht und so legt Yarn die Pakete in einem Verzeichnis mit dem Namen .yarn/cache in Form von ZIP-Dateien ab. Dieses Verzeichnis können Sie, ähnlich wie das node_modules-Verzeichnis, in Ihre .gitignore-Datei aufnehmen und so Ihr Repository klein und übersichtlich halten. Holen Sie sich den Quellcode einer solchen Yarn-Plug’n’Play-Applikation, müssen Sie yarn install ausführen, um die Pakete zu installieren.

Zero-Installs – kein yarn install mehr

Committen Sie das .yarn-Verzeichnis mit seinem lokalen Cache in Ihr Repository, verfügt Ihre Applikation plötzlich über ein Feature mit dem Namen Zero-Installs. Der Aufruf von yarn install ist nicht weiter erforderlich, da die Abhängigkeiten bereits vorhanden sind. Warum sollte man nach all den Jahren mit den ignorierten node_modules plötzlich seine Abhängigkeiten doch im Repository haben wollen? Ganz einfach: Die Funktionsfähigkeit des Quellcodes ist sichergestellt – sobald Sie den Code haben, können Sie ihn ausführen. Sie müssen sich nicht mehr auf eine bestehende Internetverbindung, Yarn als Paketmanager und das Paketrepository verlassen. Außerdem liegen die Pakete in komprimierter Form im Cache, was weiter Speicherplatz spart und das Repository so nicht unnötig aufbläht.

Was lernen wir daraus?

Yarn ist kein Newcomer in der JavaScript-Welt. Der Paketmanager hat mittlerweile einige Jahre auf dem Buckel und hat sich vielfach im produktiven Einsatz bewährt. Bei den Features kann Yarn mindestens mit npm mithalten, in manchen Aspekten ist Yarn sogar besser, andere Funktionalitäten gibt es in Yarn (noch) nicht. Der Überblick, den ich Ihnen hier gegeben habe, deckt natürlich nicht den gesamten Funktionsumfang von Yarn ab. Interessant ist beispielsweise das Workspaces-Feature, mit dem Sie umfangreiche Applikationen in einem Monorepo hervorragend verwalten können.

Falls Sie Yarn also noch nicht ausprobiert haben, ist jetzt ein hervorragender Zeitpunkt, das nachzuholen.

Immer auf dem Laufenden bleiben!
Alle News & Updates: